/**
 * @file log.c
 * @author lcz (you@domain.com)
 * @brief
 * @version 0.1
 * @date 2022-10-20
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "tools/log.h"
#include "comm/cpu_instr.h"
#include "tools/log.h"
#include "os.cfg.h"
#include <stdarg.h>
#include "tools/kilb.h"
#include "ipc/mutex.h"
#include "dev/console.h"
#include "dev/dev.h"
#define LOG_USE_COM 0

// 目标用串口，参考资料：https://wiki.osdev.org/Serial_Ports
#define COM1_PORT 0x3F8 // RS232端口0初始化
static mutex_t mutex;

static int log_dev_id;
static int log_dev_minor_id =0;


/**
 * @brief 初始化日志输出
 */
void log_init(void)
{
    log_dev_id = dev_open(DEV_TTY, log_dev_minor_id, (void *)0);
#if LOG_USE_COM
    outb(COM1_PORT + 1, 0x00); // Disable all interrupts
    outb(COM1_PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
    outb(COM1_PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
    outb(COM1_PORT + 1, 0x00); //                  (hi byte)
    outb(COM1_PORT + 3, 0x03); // 8 bits, no parity, one stop bit
    outb(COM1_PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold

    // If serial is not faulty set it in normal operation mode
    // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled)
    outb(COM1_PORT + 4, 0x0F);
#endif
    mutex_init(&mutex);
}

void kernel_itoa(char *buf, int num, int base)
{
    if (!(base == 2 || base == 8 || base == 10 || base == 16))
    {
        return;
    }
    static const char *num2ch = {"0123456789ABCDEF"};
    int new_num = num;
    char *p = buf;

    if (new_num < 0)
    {
        *p++ = '-';
        new_num = -new_num;
    }

    do
    {
        char ch = num2ch[new_num % base];
        *p++ = ch;
        new_num /= base;
    } while (new_num);
    *p-- = '\0';

    // 逆序
    char *start = (num > 0) ? buf : buf + 1;
    while (start < p)
    {
        char ch = *start;
        *start = *p;
        *p = ch;
        p--;
        start++;
    }
}
/**
 * @brief 格式化字符串到缓存中
 */
void kernel_printf(char *buf, const char *fmt, ...)
{

    // handle mutil parameters
    va_list args;
    // 将参数fmt后的参数取出到args中
    va_start(args, fmt);
    // 将fmt根据args格式化到str_buf
    kernel_vsprintf(buf, fmt, args);
    // close args
    va_end(args);
}
/**
 * 格式化字符串
 */
void kernel_vsprintf(char *buf, const char *fmt, va_list args)
{
    enum
    {
        NORMAL,
        READFMT
    } state = NORMAL;
    char *curr = buf;
    char ch;
    while ((ch = *fmt++))
    {
        switch (state)
        {
        case NORMAL:
            if (ch == '%')
            {
                state = READFMT;
            }
            else
            {
                *curr++ = ch;
            }
            break;
        case READFMT:
            if (ch == 's')
            {
                const char *str = va_arg(args, char *);
                int len = kernel_strlen(str);
                while (len--)
                {
                    *curr++ = *str++;
                }
            }
            else if (ch == 'd')
            {
                int num = va_arg(args, int);
                kernel_itoa(curr, num, 10);
                curr += kernel_strlen(curr);
            }
            else if (ch == 'x')
            {
                int num = va_arg(args, int);
                kernel_itoa(curr, num, 16);
                curr += kernel_strlen(curr);
            }
            else if (ch == 'c')
            {
                char ch = va_arg(args, int);
                *curr++ = ch;
            }
            state = NORMAL;
            break;
        }
    }
    *curr = '\0';
}

/**
 * @brief 日志打印
 */
void log_printf(const char *fmt, ...)
{
    char str_buf[128];
    kernel_memset(str_buf, 0, sizeof(str_buf));
    // handle mutil parameters
    va_list args;
    // 将参数fmt后的参数取出到args中
    va_start(args, fmt);
    // 将fmt根据args格式化到str_buf
    kernel_vsprintf(str_buf, fmt, args);
    // close args
    va_end(args);
    // modify const char *p  become char *p
    // close interrupts, protect Critical resource
    // irq_state_t state=irq_enter_protection();
    mutex_lock(&mutex);
#if LOG_USE_COM
    char *p = str_buf;
    while (*p != '\0')
    {
        while ((inb(COM1_PORT + 5) & (1 << 6)) == 0)
            ;
        outb(COM1_PORT, *p++);
    }

    outb(COM1_PORT, '\r');
    outb(COM1_PORT, '\n');
#else
    dev_write(log_dev_id, log_dev_minor_id, "log:", 4);
    dev_write(log_dev_id, log_dev_minor_id, str_buf, kernel_strlen(str_buf));
    char c = '\n';
    dev_write(log_dev_id, log_dev_minor_id, &c, 1);
    // console_write(0, str_buf, kernel_strlen(str_buf));
    // char c = '\n';
    // console_write(0, &c, 1);

#endif
    // open interrupts
    // irq_leave_protection(state);
    mutex_unlock(&mutex);
}
